package com.htstar.ovms.device.controller;


import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.htstar.ovms.common.core.i18n.MsgCode;
import com.htstar.ovms.common.core.util.OvmDateUtil;
import com.htstar.ovms.common.core.util.R;
import com.htstar.ovms.common.core.util.WebUtils;
import com.htstar.ovms.common.log.annotation.SysLog;
import com.htstar.ovms.common.security.service.OvmsUser;
import com.htstar.ovms.common.security.util.SecurityUtils;
import com.htstar.ovms.device.api.dto.DeviceDTO;
import com.htstar.ovms.device.api.dto.DeviceExportDTO;
import com.htstar.ovms.device.api.entity.Device;
import com.htstar.ovms.device.api.entity.DeviceSim;
import com.htstar.ovms.device.api.vo.DeviceDataVO;
import com.htstar.ovms.device.api.vo.DeviceImportVO;
import com.htstar.ovms.device.api.vo.ImportVO;
import com.htstar.ovms.device.service.DeviceService;
import com.htstar.ovms.device.service.DeviceSimService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.ParseException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.time.format.DateTimeFormatter;
import java.util.*;


/**
 * 设备
 *
 * @author flr
 * @date 2020-06-09 11:25:24
 */
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/device")
@Api(value = "device", tags = "设备管理/Equipment management")
public class DeviceController {

    private final DeviceService deviceService;
    private final DeviceSimService deviceSimService;

    @ApiOperation(value = "检查设备是否可以绑定/Check if the device can be bound", notes = "检查设备是否可以绑定")
    @GetMapping("/checkBinding/{deviceSn}")
    public R checkBinding(@PathVariable("deviceSn") String deviceSn) {
        return deviceService.checkDeviceIsBinding(deviceSn);
    }

    /**
     * 分页查询
     *
     * @param deviceDTO 设备查询参数
     * @return
     */
    @ApiOperation(value = "分页查询/Paging query", notes = "分页查询")
    @PostMapping("/page")
    public R<Page<DeviceDataVO>> getDevicePage(@RequestBody DeviceDTO deviceDTO) {
        return R.ok(deviceService.selectDevicePage(deviceDTO));
    }

    /**
     * OTA分页查询
     *
     * @param deviceDTO 设备查询参数
     * @return
     */
    @ApiOperation(value = "分页查询/OTA paging query", notes = "分页查询")
    @PostMapping("/pageOTARecord")
    public R<Page<DeviceDataVO>> pageOTARecord(@RequestBody DeviceDTO deviceDTO) {
        return R.ok(deviceService.pageOTA(deviceDTO));
    }


    /**
     * 分页查询
     *
     * @param deviceDTO 设备查询参数
     * @return
     */
    @ApiOperation(value = "分页查询设备/paging query equipment", notes = "分页查询设备")
    @PostMapping("/bandPage")
    public R<Page<DeviceDataVO>> getDevicePages(@RequestBody DeviceDTO deviceDTO) {
        return R.ok(deviceService.getbandDeviceSPage(deviceDTO));
    }

    /**
     * 通过deviceSn查询设备
     *
     * @param deviceSn 设备编号
     * @return R
     */
    @ApiOperation(value = "通过deviceSn查询设备/query device through device sn", notes = "通过deviceSn查询设备")
    @GetMapping("/{deviceSn}")
    public R getById(@PathVariable("deviceSn") String deviceSn) {
        return R.ok(deviceService.selectDeviceDataVOByDeviceSn(deviceSn));
    }

    /**
     * 新增设备
     *
     * @param device 设备
     * @return R
     */
    @ApiOperation(value = "新增设备/new equipment", notes = "新增设备")
    @SysLog("新增设备")
    @PostMapping
//    @PreAuthorize("@pms.hasPermission('device_device_add')")
    public R save(@RequestBody Device device) {
        return deviceService.saveDevice(device);
    }

    /**
     * 编辑设备
     *
     * @param device 设备
     * @return R
     */
    @ApiOperation(value = "编辑设备/edit device", notes = "编辑设备")
    @SysLog("编辑设备")
    @PutMapping
//    @PreAuthorize("@pms.hasPermission('device_device_edit')")
    public R updateById(@RequestBody Device device) {
        return deviceService.updateDevice(device);
    }

    /**
     * 通过设备编号批量或单个删除设备
     *
     * @param deviceSns deviceSns
     * @return R
     */
    @ApiOperation(value = "通过设备编号批量或单个删除设备/Delete devices in batch or individually by device number", notes = "通过设备编号批量或单个删除设备")
    @SysLog("通过设备编号批量或单个删除设备")
    @DeleteMapping("/removeByDeviceSns")
//    @PreAuthorize("@pms.hasPermission('device_device_del')")
    public R removeByDeviceSns(@RequestParam(value = "deviceSns") String deviceSns) {
        return deviceService.removeByDeviceSns(deviceSns);
    }

    /**
     * 通过deviceSns批量或单个解除设备绑定
     *
     * @param deviceSns
     * @param isClearAll 1 全部清除 默认不清除
     * @return
     */
    @ApiOperation(value = "通过deviceSns批量或单个解除设备绑定/Unbind devices in batches or individually through device Sns", notes = "通过deviceSns批量或单个解除设备绑定")
    @SysLog("通过deviceSns批量或单个解除设备绑定")
    @DeleteMapping("/removeBindingByDeviceSn")
    public R removeBindingByDeviceSn(@RequestParam(value = "deviceSns") String deviceSns,
                                     @RequestParam(value = "isClearAll", required = false) Integer isClearAll) {
        return R.ok(deviceService.removeBindingByDeviceSns(deviceSns, isClearAll));
    }


    /**
     * 下载导入模板
     */
    @ApiOperation(value = "下载导入模板/download import template", notes = "下载导入模板")
    @SysLog("下载导入模板")
    @GetMapping("/downloadTemplate")
    public void downloadTemplate(@RequestParam(value = "templateName") String templateName, HttpServletResponse response) {
        OutputStream out = null;
        InputStream inputStream = null;
        String name = null;
        if (Objects.equals(templateName, "car_fuelc_ard_template")) {
            name = "油卡模板";
        }
        if (Objects.equals(templateName, "device_template")) {
            name = "设备模板";
        }
        if (Objects.equals(templateName, "sim_template")) {
            name = "SIM卡号模板";
        }
        if (Objects.equals(templateName, "carInfo_template")) {
            name = "车辆信息模板";
        }
        if (Objects.equals(templateName, "etcInfo_template")) {
            name = "etc信息模板";
        }
        if (Objects.equals(templateName, "transfer_device_template")) {
            name = "设备迁移模板";
        }
        try {
            log.info("模板{}", name);
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + new String(URLDecoder.decode(
                    name, "UTF-8").getBytes("UTF-8"), "ISO8859-1") + ".xlsx");
//            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(name, "utf-8"));
            out = response.getOutputStream();
            inputStream = this.getClass().getResourceAsStream("/template/" + templateName + ".xlsx");
            int b = 0;
            byte[] buffer = new byte[1024];
            while ((b = inputStream.read(buffer)) != -1) {
                // 4.写到输出流(out)中
                out.write(buffer, 0, b);
            }
            out.flush();
        } catch (IOException e) {
            log.error("下载导入模板失败", e);
        } finally {
            IoUtil.close(out);
            IoUtil.close(inputStream);
        }
    }

    /**
     * 导出设备信息
     *
     * @param deviceDTO
     * @param response
     */
    @ApiOperation(value = "导出设备信息/Export device information", notes = "导出设备信息")
    @SysLog("导出设备信息")
    @PostMapping("/exportDeviceInfo")
    public void exportSimInfo(@RequestBody DeviceExportDTO deviceDTO, HttpServletResponse response) {
        List<DeviceDataVO> devices = deviceService.exportDeviceInfo(deviceDTO);
        if (CollectionUtils.isEmpty(devices)) {
            return;
        }
        ExcelWriter writer = ExcelUtil.getWriter(true);
        writer.renameSheet("设备信息");
        List<Map<String, Object>> rows = new ArrayList<>();
        for (DeviceDataVO device : devices) {
            Map<String, Object> row = new LinkedHashMap<>();
            row.put("设备序列号(*)", device.getDeviceSn() == null ? "" : device.getDeviceSn());
            row.put("设备型号", device.getLable() == null ? "" : device.getLable());
            row.put("企业名称", device.getEtpName() == null ? "" : device.getEtpName());
            row.put("车牌号", device.getLicCode() == null ? "" : device.getLicCode());
            row.put("车型", device.getCarType() == null ? "" : device.getCarType());
            row.put("车架号", device.getFrameCode() == null ? "" : device.getFrameCode());
            row.put("车辆状态", device.getOnline() == null ? "" : device.getOnline());
            row.put("绑定时间", device.getCreateTime() == null ? "" : device.getCreateTime());
            rows.add(row);
        }
        writer.write(rows, true);
        OutputStream out = null;
        try {
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + new String(URLEncoder.encode(
                    "设备信息" + OvmDateUtil.getCstNowDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
                    , "UTF-8").getBytes("UTF-8"), "ISO8859-1") + ".xlsx");
            out = response.getOutputStream();
            writer.flush(out, true);
        } catch (IOException e) {
            log.error("设备信息导出失败", e);
        } finally {
            writer.close();
            IoUtil.close(out);
        }
    }


    /**
     * 设备迁移(迁移没有使用的设备到其他企业下面)
     */
    @ApiOperation(value = "设备迁移(迁移没有使用的设备到其他企业下面)/Equipment migration (migrating unused equipment to other companies)", notes = "设备迁移(迁移没有使用的设备到其他企业下面)")
    @SysLog("设备迁移(迁移没有使用的设备到其他企业下面)")
    @PostMapping("/transferDevice")
    public R transferDevice(@RequestParam("etpId") Integer etpId,
                            @RequestParam(value = "file", required = false) MultipartFile file
            , @RequestParam(value = "deviceSns", required = false) String deviceSns) {
        boolean flag = false;
        List<Device> devices = null;
        OvmsUser user = SecurityUtils.getUser();
        if (file != null && StringUtils.isNotBlank(file.getOriginalFilename()) && file.getSize() != 0) {
            devices = com.htstar.ovms.common.excel.util.ExcelWriter.onlyOneHeadingImport(file, Device.class);
        } else {
            if (StringUtils.isNotBlank(deviceSns)) {
                devices = deviceService.list(new QueryWrapper<Device>().in("device_sn", Arrays.asList(deviceSns.split(","))));
            }
        }
        if (!CollectionUtils.isEmpty(devices)) {
            for (int i = 0; i < devices.size(); i++) {
                Device device = devices.get(i);
                device.setEtpId(etpId);
                device.setUpdateTime(OvmDateUtil.getCstNow());
                device.setUserId(user.getId());
                if (StringUtils.isNotBlank(device.getDeviceSn())) {
                    int count = deviceService.count(new QueryWrapper<Device>().eq("device_sn", device.getDeviceSn()));
                    if (count == 0) {
                        return R.failed((i + 1) + "" + MsgCode.THISLINEMIGRATIONFAILED.msg());
                    }
                    flag = deviceService.update(device, new UpdateWrapper<Device>().eq("device_sn", device.getDeviceSn()));
                } else {
                    return R.failed((i + 1) + "" + MsgCode.LINEDEVICENUMBERISEMPTY.msg());
                }
            }
        } else {
            return R.failed(MsgCode.DEVICENUMBERISEMPTY.msg());
        }
        return R.ok(flag);
    }

    public List<String> getDuplicateElements(List<Device> list, Integer s) {
        // 默认出现的次数为1
        int count = 0;
        List<String> collect = new ArrayList<>();
        int size = list.size();
        //计算下标添加到list
        List<Integer> integers = new ArrayList<>();
        //去重复
        Set<String> hashSet = new HashSet<>();
        if (Objects.equals(s, 1)) {
            for (int i = 0; i < size; i++) {
                Device device = list.get(i);
                if (!hashSet.contains(device.getSim())) {
                    integers.clear();
                    for (int j = i + 1; j < size; j++) {//与下一条对比
                        Device devices = list.get(j);
                        if (!Objects.equals(devices.getSim(), null) || !Objects.equals(device.getSim(), null)) {
                            if (Objects.equals(devices.getSim(), device.getSim())) {
                                hashSet.add(devices.getSim());
                                count++;//累加重复次数
                                integers.add((j + 2));
                            }
                        }
                    }
                    String finalStar = StringUtils.join(integers, ",");

                    if (count > 0) {//大于0说明上一条与下一条比较发型不是重复，则不用累加
                        collect.add(MsgCode.THEDUPLICATESIM.msg() +
                                device.getSim() + MsgCode.I_REPEAT.msg() + count + MsgCode.IN_TURN.msg() + finalStar + MsgCode.THAT_S_OK.msg());
                        count = 0;//重新累加下一条
                    }

                }
            }
        }
        if (Objects.equals(s, 2)) {
            for (int i = 0; i < size; i++) {
                Device device = list.get(i);
                if (!hashSet.contains(device.getDeviceSn())) {
                    integers.clear();
                    for (int j = i + 1; j < size; j++) {
                        Device devices = list.get(j);
                        if (!Objects.equals(devices.getDeviceSn(), null) || !Objects.equals(device.getDeviceSn(), null)) {
                            if (Objects.equals(devices.getDeviceSn(), device.getDeviceSn())) {
                                hashSet.add(devices.getDeviceSn());
                                count++;
                                integers.add((j + 2));
                            }
                        }
                    }
                    String finalStar = StringUtils.join(integers, ",");
                    if (count > 0) {
                        collect.add(MsgCode.THE_REPEATED_DEVICE.msg() +
                                device.getDeviceSn() + MsgCode.I_REPEAT.msg() + count + MsgCode.IN_TURN.msg() + finalStar + MsgCode.THAT_S_OK.msg());
                        count = 0;
                    }
                }
            }
        }
        return collect;
    }

    /**
     * 导入设备信息
     *
     * @param file
     * @param
     * @param
     * @return
     */
    @ApiOperation(value = "导入设备信息/import device information", notes = "导入设备信息")
    @SysLog("导入设备信息")
    @PostMapping("/importDeviceInfo")
    @Transactional(rollbackFor = Exception.class)
    public R importSimInfo(@RequestParam(value = "file") MultipartFile file) {
        boolean flag = true;
        OvmsUser user = SecurityUtils.getUser();
        List<Device> list = new ArrayList<>();
        List<DeviceSim> lists = new ArrayList<>();
        List<String> vos = new ArrayList<>();
        List<Device> devices = com.htstar.ovms.common.excel.util.ExcelWriter.onlyOneHeadingImport(file, Device.class);
        if (!CollectionUtils.isEmpty(devices)) {
            for (int i = 0; i < devices.size(); i++) {
                Device device = devices.get(i);
                if (Objects.equals(device.getDeviceSn(), null)) {
                    flag = false;
                    vos.add((i + 2) + "" + MsgCode.PLEASECHANGEITCORRECTLYANDTRY.msg());
                }
                int count = deviceService.count(new QueryWrapper<Device>().eq("device_sn", device.getDeviceSn()));
                if (count != 0) {
                    DeviceImportVO a = new DeviceImportVO();
                    flag = false;
                    vos.add((i + 2) + "" + MsgCode.PLEASE_CORRECT_IT.msg());
                    //return R.failed("第" + (i + 1) + "行设备编号数据库已经存在,导入失败，请更改正确再试");
                }
                LambdaQueryWrapper<DeviceSim> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(DeviceSim::getSim, device.getSim());
                DeviceSim one = deviceSimService.getOne(wrapper);
                device.setProductType(1);
                device.setEtpId(SecurityUtils.getUser().getEtpId());
                device.setUserId(user.getId());
                list.add(device);
                //flag = deviceService.save(device);

                if (Objects.equals(one, null)) {
//                    if (Objects.equals(device.getSim(), null)) {
//                        flag = false;
//                        vos.add("第" + (i + 2) + "行该设备编号数据库有不存在的SIM卡号");
//                    }
                    if (!Objects.equals(device.getSim(), null) && !Objects.equals(device.getSim(), "")) {
                        flag = false;
                        vos.add((i + 2) + "" + MsgCode.EXISTINTHEDEVICENUMBERDATABASE.msg());
                    }
                } else {
                    if (!Objects.equals(one.getEtpId(), SecurityUtils.getUser().getEtpId())) {
                        flag = false;
                        vos.add((i + 2) + "" + MsgCode.IMPORTFAILEDPLEASECORRECT.msg());
                    }
                    if (!Objects.equals(one.getDeviceSn(), null) && !Objects.equals(one.getDeviceSn(), "")) {
                        flag = false;
                        vos.add((i + 2) + "" + MsgCode.SIMCARDNUMBERHASBEENBOUND.msg());
                        //return  R.failed("第" + (i + 1) +"该设备编号的SIM卡号已经被绑定");
                    } else {
//                           LambdaUpdateWrapper<DeviceSim> wrapper1 = new LambdaUpdateWrapper<>();
//                           wrapper1.eq(DeviceSim::getSim,device.getSim())
//                                   .set(DeviceSim::getDeviceSn,device.getDeviceSn());
                        DeviceSim sim = new DeviceSim();
                        sim.setId(one.getId());
                        sim.setDeviceSn(device.getDeviceSn());
                        if (!Objects.equals(one.getStartTime(),null )) {
                            sim.setStartTime(one.getStartTime());
                            sim.setEndTime(one.getEndTime());
                        }
                        lists.add(sim);
                        //deviceSimService.update(wrapper1);
                    }
                }
            }
            List<String> duplicateValue = getDuplicateElements(devices, 1);
            List<String> duplicateValues = getDuplicateElements(devices, 2);
            if (duplicateValue.size() > 0) {
                flag = false;
                for (String s : duplicateValue) {
                    vos.add(s);
                }
            }
            if (duplicateValues.size() > 0) {
                flag = false;
                for (String s : duplicateValues) {
                    vos.add(s);
                }
            }
        } else {
            return R.failed(MsgCode.EQUIPMENTEXCELTEMPLATEFORMAT.msg());
        }

        if (flag) {
            flag = deviceService.saveBatch(list);
            deviceSimService.updateBatchById(lists);
            return R.ok(flag);
        }

        DeviceImportVO v = new DeviceImportVO();
        List<ImportVO> i = new ArrayList<>();
        vos.forEach(s -> {
            ImportVO va = new ImportVO();
            va.setName(s);
            i.add(va);
        });
        v.setError(i);

        return R.faileds(v, MsgCode.MAKECHANGESWHILEIMPORTING.msg());
    }

    public R importerror(List<String> list) {
        HttpServletResponse response = WebUtils.getResponse();
        ExcelWriter writer = ExcelUtil.getWriter(true);
        writer.renameSheet("导入--错误信息");
        List<Map<String, Object>> rows = new ArrayList<>();
        for (String device : list) {
            Map<String, Object> row = new LinkedHashMap<>();
            row.put("错误信息", device);
            rows.add(row);
        }
        writer.write(rows, true);
        OutputStream out = null;
        try {
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + new String(URLEncoder.encode(
                    "行驶报表-按人员统计" + OvmDateUtil.getCstNowDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
                    , "UTF-8").getBytes("UTF-8"), "ISO8859-1") + ".xlsx");
            out = response.getOutputStream();
            writer.flush(out, true);
        } catch (IOException e) {
            log.error("设备信息导出失败", e);
        } finally {
            writer.close();
            IoUtil.close(out);
            return R.failed();
        }

    }


}
